import { lastValueFrom } from "rxjs";
import { Injectable } from "@nestjs/common";
import { ClientProxy, ClientProxyFactory, Transport, Closeable } from "@nestjs/microservices";

import {
  AppLan,
  ENUM_APP,
  AppLanRpcInterface,
  AppMessageRpcInterface,
  AppFileRpcInterface,
  getLanRpcDto,
  getLanRpcRes,
  AppLanGateway,
  putLanRpcDto,
  putLanRpcRes,
  AppMessageGateway,
  getMessageRpcDto,
  getMessageRpcRes,
  putMessageRpcDto,
  putMessageRpcRes,
  AppFileGateway,
  getFileRpcDto,
  getFileRpcRes,
} from "qqlx-core";
import { getAppTrans, toNumber, toString } from "qqlx-cdk";

import { Logger } from "@lib/log";
import { RpcWaiting } from "./rpc.response";
import { getRandomString } from "@lib/crypto";

@Injectable()
export class BusRpc implements AppLanRpcInterface, AppMessageRpcInterface, AppFileRpcInterface {
  constructor() {
    this.connect();
  }

  onModuleDestroy() {
    if (this.VISIBLE_TIMER) clearInterval(this.VISIBLE_TIMER);
    this.CLIENT?.close();
  }

  // ====== 读写服务配置 ======
  // ====== 读写服务配置 ======
  // ====== 读写服务配置 ======

  getSchema(): AppLan {
    return { ip: "", port: -1, message: "" };
  }

  @RpcWaiting(`${AppLanGateway.path}/get`, { logger: false })
  async getLan(dto: getLanRpcDto) {
    const ob = this.CLIENT.send(`${AppLanGateway.path}/get`, dto);
    return lastValueFrom<getLanRpcRes>(ob);
  }

  @RpcWaiting(`${AppLanGateway.path}/put`, { logger: false }) // 服务进程会定时推送，所以不要长期记录日志
  async putLan(dto: putLanRpcDto) {
    const ob = this.CLIENT.send(`${AppLanGateway.path}/put`, dto);
    return lastValueFrom<putLanRpcRes>(ob);
  }

  // ====== 读写文字配置 ======
  // ====== 读写文字配置 ======
  // ====== 读写文字配置 ======

  @RpcWaiting(`${AppMessageGateway.path}/get`, { logger: false })
  async getMessage(dto: getMessageRpcDto) {
    const ob = this.CLIENT.send(`${AppMessageGateway.path}/get`, dto);
    return lastValueFrom<getMessageRpcRes>(ob);
  }

  @RpcWaiting(`${AppMessageGateway.path}/put`, { logger: false })
  async putMessage(dto: putMessageRpcDto) {
    const ob = this.CLIENT.send(`${AppMessageGateway.path}/put`, dto);
    return lastValueFrom<putMessageRpcRes>(ob);
  }

  // ====== 读写文件配置 ======
  // ====== 读写文件配置 ======
  // ====== 读写文件配置 ======

  @RpcWaiting(`${AppFileGateway.path}/get`)
  async getFile(dto: getFileRpcDto) {
    const ob = this.CLIENT.send(`${AppFileGateway.path}/get`, dto);
    return lastValueFrom<getFileRpcRes>(ob);
  }

  // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░░░░▄▄▄▄▄▄░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░░▄█▀▀▀▀▀█▄░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░▄█░░▄▄▄░░█▄░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░█░░█▄▄█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░▀▄▀▄▄▄▄▄▀▄▀░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 在主进程入口中使用

  private VISIBLE_TIMER!: NodeJS.Timeout;
  private REMOTE_HOST!: string;
  private REMOTE_PORT!: number;

  /** 在应用启动的时候，将当前APP的微服务地址（永远/10s）持续同步到调度中心
   * @app 当 Bus 调度中心，在因为更新、意外情况导致服务中断重启之后
   */
  keepAppVisible(app: ENUM_APP, net?: AppLan) {
    this.REMOTE_HOST = net?.ip || this.REMOTE_HOST;
    this.REMOTE_PORT = net?.port || this.REMOTE_PORT;

    this._doAppVisible(app);
    this.VISIBLE_TIMER = setInterval(() => this._doAppVisible(app), 1000 * 10);
  }

  private async _doAppVisible(app: ENUM_APP) {
    const app_name = getAppTrans(app)!.zh;
    const droplet_bus_name = getAppTrans(ENUM_APP.DROPLET_BUS)!.zh;

    try {
      const schema = this.getSchema();
      schema.ip = this.REMOTE_HOST;
      schema.port = this.REMOTE_PORT;
      await this.putLan({ app, schema });

      Logger.trace({ log: `👀已曝光 ${app_name} >>> Remote ${droplet_bus_name} Service` });
    } catch (error) {
      Logger.trace({ log: `👀❌曝光失败 ${app_name} >>> Remote ${droplet_bus_name} Service`, detail: error as any });

      /** 重新创建TCP链接 */
      this.connect();
    }
  }

  // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░░░░▄▄▄▄▄▄░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░░▄█▀▀▀▀▀█▄░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░▄█░░▄▄▄░░█▄░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░█░░█▄▄█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░▀▄▀▄▄▄▄▄▀▄▀░░░░░░░░░░░░░░░░░░░░░░░░░░
  // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

  public getClientId() {
    return this.CLIENT_ID;
  }

  private CLIENT!: ClientProxy & Closeable;
  private CLIENT_ID = "";
  private CLIENT_NAME = "";

  private connect() {
    const remote_app = ENUM_APP.DROPLET_BUS;
    this.CLIENT_ID = getRandomString(6);
    this.CLIENT_NAME = `Local ${getAppTrans(remote_app)!.zh} RPC Connection（${this.CLIENT_ID}）`;

    const host = toString(process.argv[2]).replace(/\s+/g, "");
    const port = toNumber(process.argv[3]);
    if (!host) throw { message: `在命令行参数中，找不到配置中心的地址` };
    if (port <= 0) throw { message: `在命令行参数中，找不到配置中心的端口` };

    if (this.CLIENT) this.CLIENT.close();
    this.CLIENT = ClientProxyFactory.create({
      transport: Transport.TCP,
      options: { host, port },
    });
    Logger.info({ log: `🚛已创建TCP链接：${this.CLIENT_NAME}` });
  }
}
